PIC Tutorial Two - SwitchesFor the first parts of this tutorial you require the Main Board and the Switch Board, the later parts will also use the LED Board, as written the tutorials use the Switch Board on PortA and the LED Board on PortB. Download zipped tutorial files. Tutorial 2.1 - requires Main Board and Switch Board. This simple program turns the corresponding LED on, when the button opposite it is pressed, extinguishing all other LED's. ;Tutorial 2.1 - Nigel Goodwin 2002 LIST p=16F628 ;tell assembler what chip we are using include "P16F628.inc" ;include the defaults for the chip __config 0x3D18 ;sets the configuration settings (oscillator type etc.) LEDPORT Equ PORTA ;set constant LEDPORT = 'PORTA' SWPORT Equ PORTA ;set constant SWPORT = 'PORTA' LEDTRIS Equ TRISA ;set constant for TRIS register SW1 Equ 7 ;set constants for the switches SW2 Equ 6 SW3 Equ 5 SW4 Equ 4 LED1 Equ 3 ;and for the LED's LED2 Equ 2 LED3 Equ 1 LED4 Equ 0 ;end of defines org 0x0000 ;org sets the origin, 0x0000 for the 16F628, ;this is where the program starts running movlw 0x07 movwf CMCON ;turn comparators off (make it like a 16F84) bsf STATUS, RP0 ;select bank 1 movlw b'11110000' ;set PortA 4 inputs, 4 outputs movwf LEDTRIS bcf STATUS, RP0 ;select bank 0 clrf LEDPORT ;set all outputs low Loop btfss SWPORT, SW1 call Switch1 btfss SWPORT, SW2 call Switch2 btfss SWPORT, SW3 call Switch3 btfss SWPORT, SW4 call Switch4 goto Loop Switch1 clrf LEDPORT ;turn all LED's off bsf SWPORT, LED1 ;turn LED1 on retlw 0x00 Switch2 clrf LEDPORT ;turn all LED's off bsf SWPORT, LED2 ;turn LED2 on retlw 0x00 Switch3 clrf LEDPORT ;turn all LED's off bsf SWPORT, LED3 ;turn LED3 on retlw 0x00 Switch4 clrf LEDPORT ;turn all LED's off bsf SWPORT, LED4 ;turn LED4 on retlw 0x00 end As with the previous tutorials we first set things up, then the main program runs in a loop, the first thing the loop does is check switch SW1 with the 'btfss SWPORT, SW1' line, if the switch isn't pressed the input line is held high by the 10K pull-up resistor and it skips the next line. This takes it to the 'btfss SWPORT, SW2' line, where SW2 is similarly checked - this continues down checking all the switches and then loops back and checks them again. If a key is pressed, the relevant 'btfss' doesn't skip the next line, but instead calls a sub-routine to process the key press, each switch has it's own sub-routine. These sub-routines are very simple, they first 'clrf' the output port, turning all LED's off, and then use 'bsf' to turn on the corresponding LED, next the sub-routine exits via the 'retlw' instruction. As the switch is likely to be still held down, the same routine will be run again (and again, and again!) until you release the key, however for this simple application that isn't a problem and you can't even tell it's happening. Tutorial 2.2 - requires Main Board and Switch Board. This program toggles the corresponding LED on and off, when the button opposite it is pressed. It introduces the concept of 'de-bouncing' - a switch doesn't close immediately, it 'bounces' a little before it settles, this causes a series of fast keypresses which can cause chaos in operating a device. ;Tutorial 2.2 - Nigel Goodwin 2002 LIST p=16F628 ;tell assembler what chip we are using include "P16F628.inc" ;include the defaults for the chip __config 0x3D18 ;sets the configuration settings (oscillator type etc.) cblock 0x20 ;start of general purpose registers count1 ;used in delay routine counta ;used in delay routine countb ;used in delay routine endc LEDPORT Equ PORTA ;set constant LEDPORT = 'PORTA' SWPORT Equ PORTA ;set constant SWPORT = 'PORTA' LEDTRIS Equ TRISA ;set constant for TRIS register SW1 Equ 7 ;set constants for the switches SW2 Equ 6 SW3 Equ 5 SW4 Equ 4 LED1 Equ 3 ;and for the LED's LED2 Equ 2 LED3 Equ 1 LED4 Equ 0 SWDel Set Del50 ;set the de-bounce delay (has to use 'Set' and not 'Equ') ;end of defines org 0x0000 ;org sets the origin, 0x0000 for the 16F628, ;this is where the program starts running movlw 0x07 movwf CMCON ;turn comparators off (make it like a 16F84) bsf STATUS, RP0 ;select bank 1 movlw b'11110000' ;set PortA 4 inputs, 4 outputs movwf LEDTRIS bcf STATUS, RP0 ;select bank 0 clrf LEDPORT ;set all outputs low Loop btfss SWPORT, SW1 call Switch1 btfss SWPORT, SW2 call Switch2 btfss SWPORT, SW3 call Switch3 btfss SWPORT, SW4 call Switch4 goto Loop Switch1 call SWDel ;give switch time to stop bouncing btfsc SWPORT, SW1 ;check it's still pressed retlw 0x00 ;return is not btfss SWPORT, LED1 ;see if LED1 is already lit goto LED1ON goto LED1OFF LED1ON bsf LEDPORT, LED1 ;turn LED1 on call SWDel btfsc SWPORT, SW1 ;wait until button is released retlw 0x00 goto LED1ON LED1OFF bcf LEDPORT, LED1 ;turn LED1 on call SWDel btfsc SWPORT, SW1 ;wait until button is released retlw 0x00 goto LED1OFF Switch2 call SWDel ;give switch time to stop bouncing btfsc SWPORT, SW2 ;check it's still pressed retlw 0x00 ;return is not btfss SWPORT, LED2 ;see if LED2 is already lit goto LED2ON goto LED2OFF LED2ON bsf LEDPORT, LED2 ;turn LED2 on call SWDel btfsc SWPORT, SW2 ;wait until button is released retlw 0x00 goto LED2ON LED2OFF bcf LEDPORT, LED2 ;turn LED2 on call SWDel btfsc SWPORT, SW2 ;wait until button is released retlw 0x00 goto LED2OFF Switch3 call SWDel ;give switch time to stop bouncing btfsc SWPORT, SW3 ;check it's still pressed retlw 0x00 ;return is not btfss SWPORT, LED3 ;see if LED3 is already lit goto LED3ON goto LED3OFF LED3ON bsf LEDPORT, LED3 ;turn LED3 on call SWDel btfsc SWPORT, SW3 ;wait until button is released retlw 0x00 goto LED3ON LED3OFF bcf LEDPORT, LED3 ;turn LED3 on call SWDel btfsc SWPORT, SW3 ;wait until button is released retlw 0x00 goto LED3OFF Switch4 call SWDel ;give switch time to stop bouncing btfsc SWPORT, SW4 ;check it's still pressed retlw 0x00 ;return is not btfss SWPORT, LED4 ;see if LED4 is already lit goto LED4ON goto LED4OFF LED4ON bsf LEDPORT, LED4 ;turn LED4 on call SWDel btfsc SWPORT, SW4 ;wait until button is released retlw 0x00 goto LED4ON LED4OFF bcf LEDPORT, LED4 ;turn LED4 on call SWDel btfsc SWPORT, SW4 ;wait until button is released retlw 0x00 goto LED4OFF ;modified Delay routine, direct calls for specified times ;or load W and call Delay for a custom time. Del0 retlw 0x00 ;delay 0mS - return immediately Del1 movlw d'1' ;delay 1mS goto Delay Del5 movlw d'5' ;delay 5mS goto Delay Del10 movlw d'10' ;delay 10mS goto Delay Del20 movlw d'20' ;delay 20mS goto Delay Del50 movlw d'50' ;delay 50mS goto Delay Del100 movlw d'100' ;delay 100mS goto Delay Del250 movlw d'250' ;delay 250 ms Delay movwf count1 d1 movlw 0xC7 ;delay 1mS movwf counta movlw 0x01 movwf countb Delay_0 decfsz counta, f goto $+2 decfsz countb, f goto Delay_0 decfsz count1 ,f goto d1 retlw 0x00 end In order to de-bounce the keypresses we delay for a short time, then check that the button is still pressed, the time delay is set by the variable SWDel, which is defined as Del50 in the defines section at the start of the program. I've extended the Delay routine to provide a selection of different delays (from 0mS to 250mS), called by simple 'call' instructions, the Delay routine itself can also be called directly - simply load the required delay into the W register and 'call Delay'. We then check to see if the corresponding LED is lit, with 'btfss SWPORT, LEDx', and jump to either 'LEDxON' or 'LEDxOFF', these routines are almost identical, the only difference being that the first turns the LED on, and the second turns it off. They first switch the LED, on or off, depending on which routine it is, and then delay again (calling SWDel as before), next they check to see if the button is still pressed, looping back around if it is. Once the key has been released the routine exits via the usual 'retlw' and returns to waiting for a keypress. I've used the variable SWDel (and provided the various delay times) so that you can easily try the effect of different delay times - in particular try setting SWDel to Del0, and see how the button pressing isn't reliable, you will probably find one of the buttons is worse than the others - particularly if you use old switches, wear makes them bounce more. Tutorial 2.3 - requires Main Board, Switch Board, and LED Board. Now for a more realistic example - this combines Tutorial 2.1 with Tutorial 1.9, the result is an LED sequencing program with four different patterns, selected by the four keys, with the key selected indicated by the corresponding LED. ;Tutorial 2.3 - Nigel Goodwin 2002 LIST p=16F628 ;tell assembler what chip we are using include "P16F628.inc" ;include the defaults for the chip __config 0x3D18 ;sets the configuration settings (oscillator type etc.) cblock 0x20 ;start of general purpose registers count ;used in table read routine count1 ;used in delay routine counta ;used in delay routine countb ;used in delay routine endc LEDPORT Equ PORTB ;set constant LEDPORT = 'PORTB' LEDTRIS Equ TRISB ;set constant for TRIS register SWPORT Equ PORTA SWTRIS Equ TRISA SW1 Equ 7 ;set constants for the switches SW2 Equ 6 SW3 Equ 5 SW4 Equ 4 LED1 Equ 3 ;and for the LED's LED2 Equ 2 LED3 Equ 1 LED4 Equ 0 org 0x0000 ;org sets the origin, 0x0000 for the 16F628, ;this is where the program starts running movlw 0x07 movwf CMCON ;turn comparators off (make it like a 16F84) bsf STATUS, RP0 ;select bank 1 movlw b'00000000' ;set PortB all outputs movwf LEDTRIS movlw b'11110000' ;set PortA 4 inputs, 4 outputs movwf SWTRIS bcf STATUS, RP0 ;select bank 0 clrf LEDPORT ;set all outputs low clrf SWPORT bsf SWPORT, LED1 ;set initial pattern Start clrf count ;set counter register to zero Read movf count, w ;put counter value in W btfsc SWPORT, LED1 ;check which LED is lit call Table1 ;and read the associated table btfsc SWPORT, LED2 call Table2 btfsc SWPORT, LED3 call Table3 btfsc SWPORT, LED4 call Table4 movwf LEDPORT call Delay incf count, w xorlw d'14' ;check for last (14th) entry btfsc STATUS, Z goto Start ;if start from beginning incf count, f ;else do next goto Read Table1 ADDWF PCL, f ;data table for bit pattern retlw b'10000000' retlw b'01000000' retlw b'00100000' retlw b'00010000' retlw b'00001000' retlw b'00000100' retlw b'00000010' retlw b'00000001' retlw b'00000010' retlw b'00000100' retlw b'00001000' retlw b'00010000' retlw b'00100000' retlw b'01000000' Table2 ADDWF PCL, f ;data table for bit pattern retlw b'11000000' retlw b'01100000' retlw b'00110000' retlw b'00011000' retlw b'00001100' retlw b'00000110' retlw b'00000011' retlw b'00000011' retlw b'00000110' retlw b'00001100' retlw b'00011000' retlw b'00110000' retlw b'01100000' retlw b'11000000' Table3 ADDWF PCL, f ;data table for bit pattern retlw b'01111111' retlw b'10111111' retlw b'11011111' retlw b'11101111' retlw b'11110111' retlw b'11111011' retlw b'11111101' retlw b'11111110' retlw b'11111101' retlw b'11111011' retlw b'11110111' retlw b'11101111' retlw b'11011111' retlw b'10111111' Table4 ADDWF PCL, f ;data table for bit pattern retlw b'00111111' retlw b'10011111' retlw b'11001111' retlw b'11100111' retlw b'11110011' retlw b'11111001' retlw b'11111100' retlw b'11111100' retlw b'11111001' retlw b'11110011' retlw b'11100111' retlw b'11001111' retlw b'10011111' retlw b'00111111' ChkKeys btfss SWPORT, SW1 call Switch1 btfss SWPORT, SW2 call Switch2 btfss SWPORT, SW3 call Switch3 btfss SWPORT, SW4 call Switch4 retlw 0x00 Switch1 clrf SWPORT ;turn all LED's off bsf SWPORT, LED1 ;turn LED1 on retlw 0x00 Switch2 clrf SWPORT ;turn all LED's off bsf SWPORT, LED2 ;turn LED2 on retlw 0x00 Switch3 clrf SWPORT ;turn all LED's off bsf SWPORT, LED3 ;turn LED3 on retlw 0x00 Switch4 clrf SWPORT ;turn all LED's off bsf SWPORT, LED4 ;turn LED4 on retlw 0x00 Delay movlw d'250' ;delay 250 ms (4 MHz clock) movwf count1 d1 call ChkKeys ;check the keys movlw 0xC7 ;delay 1mS movwf counta movlw 0x01 movwf countb Delay_0 decfsz counta, f goto $+2 decfsz countb, f goto Delay_0 decfsz count1 ,f goto d1 retlw 0x00 end The main differences here are in the Delay routine, which now has a call to check the keys every milli-second, and the main loop, where it selects one of four tables to read, depending on the settings of flag bits which are set according to which key was last pressed. Tutorial 2.4 - requires Main Board, Switch Board, and LED Board. Very similar to the last tutorial, except this one combines Tutorials 2.2 and 2.3 with Tutorial 1.9, the result is an LED sequencing program with three different patterns, selected by three of the keys, with the key selected indicated by the corresponding LED - the difference comes with the fourth switch, this selects slow or fast speeds, with the fast speed being indicated by a toggled LED. ;Tutorial 2.4 - Nigel Goodwin 2002 LIST p=16F628 ;tell assembler what chip we are using include "P16F628.inc" ;include the defaults for the chip __config 0x3D18 ;sets the configuration settings (oscillator type etc.) cblock 0x20 ;start of general purpose registers count ;used in table read routine count1 ;used in delay routine count2 ;used in delay routine counta ;used in delay routine countb countc countd speed endc LEDPORT Equ PORTB ;set constant LEDPORT = 'PORTB' LEDTRIS Equ TRISB ;set constant for TRIS register SWPORT Equ PORTA SWTRIS Equ TRISA SW1 Equ 7 ;set constants for the switches SW2 Equ 6 SW3 Equ 5 SW4 Equ 4 LED1 Equ 3 ;and for the LED's LED2 Equ 2 LED3 Equ 1 LED4 Equ 0 SWDel Set Del50 org 0x0000 ;org sets the origin, 0x0000 for the 16F628, ;this is where the program starts running movlw 0x07 movwf CMCON ;turn comparators off (make it like a 16F84) bsf STATUS, RP0 ;select bank 1 movlw b'00000000' ;set PortB all outputs movwf LEDTRIS movlw b'11110000' ;set PortA 4 inputs, 4 outputs movwf SWTRIS bcf STATUS, RP0 ;select bank 0 clrf LEDPORT ;set all outputs low clrf SWPORT ;make sure all LED's are off bsf SWPORT, LED1 ;and turn initial LED on movlw d'250' movwf speed ;set initial speed Start clrf count ;set counter register to zero Read movf count, w ;put counter value in W btfsc SWPORT, LED1 ;check which LED is on call Table1 ;and call the associated table btfsc SWPORT, LED2 call Table2 btfsc SWPORT, LED3 call Table3 movwf LEDPORT call DelVar incf count, w xorlw d'14' ;check for last (14th) entry btfsc STATUS, Z goto Start ;if start from beginning incf count, f ;else do next goto Read Table1 ADDWF PCL, f ;data table for bit pattern retlw b'10000000' retlw b'01000000' retlw b'00100000' retlw b'00010000' retlw b'00001000' retlw b'00000100' retlw b'00000010' retlw b'00000001' retlw b'00000010' retlw b'00000100' retlw b'00001000' retlw b'00010000' retlw b'00100000' retlw b'01000000' Table2 ADDWF PCL, f ;data table for bit pattern retlw b'11000000' retlw b'01100000' retlw b'00110000' retlw b'00011000' retlw b'00001100' retlw b'00000110' retlw b'00000011' retlw b'00000011' retlw b'00000110' retlw b'00001100' retlw b'00011000' retlw b'00110000' retlw b'01100000' retlw b'11000000' Table3 ADDWF PCL, f ;data table for bit pattern retlw b'01111111' retlw b'10111111' retlw b'11011111' retlw b'11101111' retlw b'11110111' retlw b'11111011' retlw b'11111101' retlw b'11111110' retlw b'11111101' retlw b'11111011' retlw b'11110111' retlw b'11101111' retlw b'11011111' retlw b'10111111' ChkKeys btfss SWPORT, SW1 call Switch1 btfss SWPORT, SW2 call Switch2 btfss SWPORT, SW3 call Switch3 btfss SWPORT, SW4 call Switch4 retlw 0x00 Switch1 bcf SWPORT, LED2 ;turn unselected LED's off bcf SWPORT, LED3 ;turn unselected LED's off bsf SWPORT, LED1 ;turn LED1 on retlw 0x00 Switch2 bcf SWPORT, LED1 ;turn unselected LED's off bcf SWPORT, LED3 ;turn unselected LED's off bsf SWPORT, LED2 ;turn LED2 on retlw 0x00 Switch3 bcf SWPORT, LED1 ;turn unselected LED's off bcf SWPORT, LED2 ;turn unselected LED's off bsf SWPORT, LED3 ;turn LED3 on retlw 0x00 Switch4 call SWDel ;give switch time to stop bouncing btfsc SWPORT, SW4 ;check it's still pressed retlw 0x00 ;return is not btfss SWPORT, LED4 ;see if LED4 is already lit goto FASTON goto FASTOFF FASTON bsf SWPORT, LED4 ;turn LED4 on movlw d'80' movwf speed ;set fast speed call SWDel btfsc SWPORT, SW4 ;wait until button is released retlw 0x00 goto FASTON FASTOFF bcf SWPORT, LED4 ;turn LED4 on movlw d'250' movwf speed ;set slow speed call SWDel btfsc SWPORT, SW4 ;wait until button is released retlw 0x00 goto FASTOFF DelVar movfw speed ;delay set by Speed movwf count1 d1 call ChkKeys ;check the keys movlw 0xC7 ;delay 1mS movwf counta movlw 0x01 movwf countb Delay_0 decfsz counta, f goto $+2 decfsz countb, f goto Delay_0 decfsz count1 ,f goto d1 retlw 0x00 ;use separate delay routines, as Del50 is called from ChkKeys ;which is called from within DelVar Del50 movlw d'50' ;delay 50mS movwf count2 d3 movlw 0xC7 ;delay 1mS movwf countc movlw 0x01 movwf countd Delay_1 decfsz countc, f goto $+2 decfsz countd, f goto Delay_1 decfsz count2 ,f goto d3 retlw 0x00 end |